package com.ansion.xlog;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.ansion.utils.ThreadUtil;
import com.ansion.log.Logger;
import com.ansion.log.VLog;
import com.ansion.time.TimeUtils;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * @author john on 2020/11/8.
 * @version 1.0
 * @desc 基于xlog存储日志
 */
public class XLogger extends Logger {
    public volatile boolean loadSuccess = false;
    private final List<LogCacheBean> logCache = new ArrayList<>();
    private final String APP_TAG = "BOT";
    //TODO 字符串加密保护
    private final String pubKey = "67beb0071c3984e11e6d6ace7c54db06c6694134a5c60325a8ef7c35a67f74f1e012540bdc03fe0f65148bb7d2917cecbce12a963c29afb5a0c9b3d8a6124b6b";

    public static void load() {

    }

    void loadJniLib() {
        android.util.Log.d("Xlog", "load xlog jni start");
        System.loadLibrary("c++_shared");
        System.loadLibrary("xlog");
        android.util.Log.d("Xlog", "load xlog jni finish");
    }

    final String TAG = "XLogger";
    //    private SimpleDateFormat timeFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS", Locale.US);
    private SimpleDateFormat dayFormat = new SimpleDateFormat("yyyyMMdd", Locale.US);

    //日志写catch的模式，异步还是同步
    private int mode = Xlog.AppednerModeAsync;
    //log文件的存储目录
    private String xLogPath;
    //log文件的前缀
    private String xLogPrefix;
    //缓存存储目录
    private String cacheDir = "";
    //缓存期限
    private int cacheDays = 0;
    private final SimpleDateFormat timeFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS", Locale.US);
    private volatile boolean inited = false;
    private volatile boolean isIniting = false;

    public XLogger(int mode, String cacheDir, int cacheDays, String xLogPath, String xLogPrefix) {
        this.mode = mode;
        this.xLogPath = xLogPath;
        this.xLogPrefix = xLogPrefix;
        this.cacheDir = cacheDir;
        this.cacheDays = cacheDays;
    }

    private void initXLog() {
        if (inited || isIniting)
            return;
        if (!isIniting)
            isIniting = true;
        ThreadUtil.runIOTask(() -> {
            try {

                VLog.donly("TAG_LAZY_LOAD", "xLog init start");
                VLog.FileLog.OPEN_LOG = true;
                loadJniLib();
                cleanupOutdatedLogFiles(cacheDir);
                cleanupOutdatedLogFiles(xLogPath);
                //release模式开启日志截取
                enableMessageFilter(!VLog.OPEN_LOG);
                //是否控制台打出，true打出，false关闭，默认关闭
//        Log.setConsoleLogOpen(false);
                //设置 Log 的具体实现，这里必须调用 Log.setLogImp(new Xlog()); 日志才会写到 Xlog 中
                Log.setLogImp(new Xlog());
                //init xlog
                Log.appenderOpen(Xlog.LEVEL_DEBUG, mode, cacheDir, xLogPath, xLogPrefix, cacheDays, pubKey);
                loadSuccess = true;
            } catch (Throwable e) {
                loadSuccess = false;
                //无法使用
                logCache.clear();
            }
            VLog.donly("TAG_LAZY_LOAD", "xLog inited");
            inited = true;
            if (loadSuccess) {//初始化成功开始写日志
                VLog.donly("TAG_LAZY_LOAD", "xlog load success,write cache log start");
                synchronized (logCache) {
                    VLog.donly("TAG_LAZY_LOAD", "xlog load success,write cache log size:" + logCache.size());
                    for (LogCacheBean bean : logCache) {
                        writeLog(bean.logLevel, bean.log);
                    }
                    logCache.clear();
                    VLog.donly("TAG_LAZY_LOAD", "write cache log finish");
                }
            }

        });


//        Log.i(LOG.LOG_TAG, "initXlog: xLogPath == " + xLogPath + ",cacheDir:" + cacheDir + ",cacheDays:" + cacheDays);
    }

    @Override
    public void log(Throwable callerStack, int callerStackIndex, int level, @NonNull String tag, @Nullable Throwable tr, @Nullable String msg, Object... args) {
        if (!showLog(level, tag))
            return;
        if (inited && !loadSuccess)//初始化失败，直接返回
            return;
        initXLog();
        StringBuilder sb = new StringBuilder();
        sb.append(timeFormat.format(new Date()));
        sb.append(' ').append(logLevelStr(level)).append(' ').append(tag).append("\t");
        appendMessage(callerStack, callerStackIndex + 1, sb, tr, msg, args);
        sb.append('\n');
        final String log = sb.toString();
        if (!loadSuccess) {
            if (!inited) {//只有未初始化的时候才需要往cache中写数据
                synchronized (logCache) {
                    logCache.add(new LogCacheBean(level, log));
                }
            }
            return;
        }
        writeLog(level, log);
    }

    private void writeLog(int level, String log) {
        switch (level) {
            case VLog.INFO:
                Log.i(APP_TAG, log);
                break;
            case VLog.VERBOSE:
                Log.v(APP_TAG, log);
                break;
            case VLog.WARN:
                Log.w(APP_TAG, log);
                break;
            case VLog.ERROR:
                Log.e(APP_TAG, log);
                break;
            case VLog.DEBUG:
                Log.d(APP_TAG, log);
                break;
        }
    }

    @Override
    public void flush() {
//        Log.i(APP_TAG, "flush log start");
        //默认开启异步flush
        if (!loadSuccess)
            return;
        Log.appenderFlushSync(true);
//        Log.i(APP_TAG, "flush log end");
    }

    //清理过期日志
    private void cleanupOutdatedLogFiles(String filePath) {
        if (TextUtils.isEmpty(filePath))
            return;
        File[] files = new File(filePath).listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        // find out files which should be kept first (latest N days)
        Set<String> keptLogDates = new HashSet<>();
        Date today = new Date();
        for (int i = 0; i < cacheDays; i++) {
            Date day = new Date(today.getTime() - TimeUtils.ONE_DAY * i);
            String fileName = dayFormat.format(day);
            android.util.Log.i(TAG, "kept file " + fileName);
            keptLogDates.add(fileName);
        }

        for (File file : files) {
            if (!isLogFile(file.getName())) {
                continue;
            }
            String fileName = file.getName();

            String fileDate = fileName.substring(fileName.lastIndexOf("_") + 1, fileName.lastIndexOf("."));
            if (!keptLogDates.contains(fileDate)) {
                android.util.Log.i(TAG, "delete file " + file.getName());
                //noinspection ResultOfMethodCallIgnored
                file.delete();
            }
        }
    }

    public boolean isLogFile(@NonNull String fileName) {
        return fileName.endsWith(".xlog");
    }

    private static class LogCacheBean {
        public int logLevel;
        public String log;

        public LogCacheBean(int logLevel, String log) {
            this.log = log;
            this.logLevel = logLevel;
        }
    }
}
